package com.apes.framework.plugin.condition.service;

import com.apes.framework.api.ApesParser;
import com.apes.framework.api.DomainService;
import com.apes.framework.jpa.BaseModel;
import com.apes.framework.jpa.repository.CustomRepository;
import com.apes.framework.jpa.spec.JpaDsl;
import com.apes.framework.plugin.cache.MemoryCache;
import com.apes.framework.plugin.condition.Bucket;
import com.apes.framework.plugin.condition.model.*;
import com.apes.framework.plugin.condition.repository.ConditionTableRepository;
import com.apes.framework.plugin.condition.repository.FieldCatalogItemRepository;
import com.apes.framework.plugin.esb.api.SimpleRequest;
import com.apes.framework.plugin.filereader.xml.ApesElement;
import com.apes.framework.plugin.filereader.xml.SimpleApesElement;
import com.apes.framework.plugin.form.parser.element.*;
import com.apes.framework.util.DateUtil;
import com.apes.framework.util.JpaUtil;
import com.apes.framework.util.ReflectUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class PricingStrategyService extends DomainService {

    @Autowired
    ConditionTableRepository conditionTableRepository;

    @Autowired
    FieldCatalogItemRepository fieldCatalogItemRepository;


    public List<ConditionTable> findAllByConditionTypesID(SimpleRequest simpleRequest) {
        String conditionTypesID = simpleRequest.get("conditionTypesID");
        List<ConditionTable> conditionTableList = conditionTableRepository.findAllByConditionTypesID(conditionTypesID);
        if (conditionTableList.size() == 0) throw new RuntimeException("未找到对应的条件表");
        return conditionTableList;
    }

    public List<Map> getFormConfigForConditionTableId(SimpleRequest simpleRequest) {
        String conditionTableId = simpleRequest.get("conditionTableId");

        return saveFormConfig(conditionTableId);
//        List<Map> cache = (List<Map>) MemoryCache.getInstance().get("tech."+conditionTableId);
//        if(cache == null) {
//            synchronized (this){
//                cache =  (List<Map>) MemoryCache.getInstance().get("tech."+conditionTableId);
//                if(cache == null) return saveFormConfig(conditionTableId);
//                return cache;
//            }
//        }
//        return cache;
    }

    private List<Map> saveFormConfig(String conditionTableId) {
        // 条件表
        ConditionTable conditionTable = conditionTableRepository.findOne(conditionTableId);
        // 获取条件表明细
        Set<ConditionTableItem> conditionFieldList = conditionTable.getConditionTableItems();

        // 设置解析器
        // sheet > group > field
        ApesElement sheetElement = createApesElement("sheet", new SheetElementParser());
        ApesParser groupParser = new GroupElementParser();
        ApesElement mainGroupElement = createApesElement("group", groupParser);
        mainGroupElement.setParent(sheetElement);
        mainGroupElement.setAttribute("bodyStyle", "{'padding':0,'margin-top':'-16px'}");
        sheetElement.setNestedElement(mainGroupElement);

        // sheet > tabs > tab > table > field
        ApesParser tabsParser = new TabsElementParser();
        ApesElement itemsGroupElement = createApesElement("tabs", tabsParser);
        ApesParser tabParser = new TabElementParser();
        ApesElement tabElemElement = createApesElement("tab", tabParser);
        tabElemElement.setAttribute("title", "记录明细");
        tabElemElement.setParent(itemsGroupElement);
        ApesElement tableElement = createApesElement("table", new TableElementParser());
        tableElement.setParent(itemsGroupElement);
        tableElement.setAttribute("domLayout", "autoHeight");
        tableElement.setAttribute("name", "items");
        tableElement.setAttribute("required", "true");
        tableElement.setAttribute("disabled", "ctx.states === 'new'");
        tableElement.setParent(tabElemElement);
        itemsGroupElement.setParent(sheetElement);
        itemsGroupElement.setNestedElement(tabElemElement);
        tabElemElement.setNestedElement(tableElement);
        sheetElement.setNestedElement(itemsGroupElement);

        ApesParser fieldParser = new FieldElementParser();
        ApesElement fieldElement;

        // 目录明细
        FieldCatalogItem fieldCatalogItem;
        // 条件字段
        ConditionField conditionField;
        // 条件表 关键字段
        for (ConditionTableItem conditionTableItem : conditionFieldList) {
            fieldCatalogItem = conditionTableItem.getFieldCatalogItem();
            conditionField = fieldCatalogItem.getConditionField();
            fieldElement = createApesElement("field", fieldParser);
            // 添加默认属性
            setFieldElementDefaultAttributeByConditionField(fieldElement, conditionField);

            // 添加目录属性
            setFiledElementAttributeByFieldCatalogItem(fieldElement, fieldCatalogItem);

            // 添加到主表或者从表
            isMainOrItems(conditionTableItem.isMain(), mainGroupElement, tableElement, fieldElement);
        }

        // 系统通用字段 有效期,下限等
        List<FieldCatalogItem> fieldCatalogItemList = fieldCatalogItemRepository.findAllByFieldCatalog_IdOrderById("general");

        // 条件表通用字段 位置：主/从表
        Set<ConditionTableGeneral> conditionTableGeneralList = conditionTable.getConditionTableGenerals();

        // 条件表的通用字段属性覆盖系统通用字段属性
        conditionTableGeneralList.stream().forEach(
                (general) -> {
                    fieldCatalogItemList.stream().filter(
                            catalogItem -> catalogItem.getId().equals(general.getFieldCatalogItem().getId())
                    ).findFirst().ifPresent(
                            (item) -> {
                                item.setMain(general.isMain());
                            }
                    );
                }
        );

        // 阶梯明细
        Set<ConditionTableStepped> conditionTableSteppedSet = conditionTable.getConditionTableSteppeds();

        // 过滤通用字段 amount 字段
        if (conditionTableSteppedSet.size() > 0) {
            fieldCatalogItemList.remove(fieldCatalogItemList.stream().filter(item -> "amount".equals(item.getConditionField().getId())).findAny().get());
        }


        // 添加通用字段
        for (FieldCatalogItem item : fieldCatalogItemList) {
            conditionField = item.getConditionField();
            fieldElement = createApesElement("field", fieldParser);
            // 添加属性
            setFieldElementDefaultAttributeByConditionField(fieldElement, conditionField);
            // 目录属性优先级高于字段属性
            setFiledElementAttributeByFieldCatalogItem(fieldElement, item);
            fieldElement.setAttribute("width", "100");
            // 添加到主表或者从表
            isMainOrItems(item.isMain(), mainGroupElement, tableElement, fieldElement);
        }


        // 添加阶梯字段
        for (ConditionTableStepped conditionTableStepped : conditionTableSteppedSet) {
            // 阶梯字段
            fieldElement = createApesElement("field", fieldParser);
            setSteppedFieldElementAttribute(fieldElement, conditionTableStepped, false);
            isMainOrItems(false, null, tableElement, fieldElement);

            // 阶梯值字段
            fieldElement = createApesElement("field", fieldParser);
            setSteppedFieldElementAttribute(fieldElement, conditionTableStepped, true);
            isMainOrItems(false, null, tableElement, fieldElement);
        }

        List<Map> map = sheetElement.parse();
        MemoryCache.getInstance().put("tech." + conditionTableId, map);
        return map;
    }


    // 设置阶梯字段属性
    private void setSteppedFieldElementAttribute(ApesElement fieldElement, ConditionTableStepped conditionTableStepped, boolean isValue) {
        fieldElement.setAttribute("label", isValue ? conditionTableStepped.getName() + "值" : conditionTableStepped.getName());
        // todo 目前先按 id
        fieldElement.setAttribute("name", isValue ? conditionTableStepped.stepValueField() : conditionTableStepped.stepKeyField());
        fieldElement.setAttribute("widget", "inputNumber");
        fieldElement.setAttribute("required", "false");
        fieldElement.setAttribute("disabled", "ctx.states === 'new'");
        fieldElement.setAttribute("width", "100");
    }


    /*
    * 通过条件字段设置默认属性
    * */
    private void setFieldElementDefaultAttributeByConditionField(ApesElement fieldElement, ConditionField conditionField) {
        fieldElement.setAttribute("label", conditionField.getName());
        fieldElement.setAttribute("name", conditionField.fieldName());
        fieldElement.setAttribute("widget", Optional.ofNullable(conditionField.getWidgetType()).map(type -> type.getId()).orElse("span"));
        fieldElement.setAttribute("disabled", "ctx.states === 'new'");
        if (conditionField.getApi() != null) {
            fieldElement.setAttribute("api", conditionField.getApi());
        }
        if (conditionField.getSearchFormId() != null) {
            fieldElement.setAttribute("searchFormId", conditionField.getSearchFormId());
        }
        if (conditionField.getValueFormatter() != null) {
            fieldElement.setAttribute("valueFormatter", conditionField.getValueFormatter());
        }
    }

    /*
    * 通过目录明细设置默认属性
    * */
    private void setFiledElementAttributeByFieldCatalogItem(ApesElement fieldElement, FieldCatalogItem fieldCatalogItem) {
        String addAttribute = fieldCatalogItem.getApi();
        if (addAttribute != null) fieldElement.setAttribute("api", addAttribute);
        addAttribute = fieldCatalogItem.getSearchFormId();
        if (addAttribute != null) fieldElement.setAttribute("searchFormId", addAttribute);
        fieldElement.setAttribute("hide", String.valueOf(!fieldCatalogItem.isShow()));
        fieldElement.setAttribute("required", String.valueOf(fieldCatalogItem.isRequired()));
    }


    /*
    * 从表还是明细
    * */
    private void isMainOrItems(boolean isMain, ApesElement mainElement, ApesElement itemsElement, ApesElement fieldElement) {
        if (isMain) {
            fieldElement.setParent(mainElement);
            mainElement.setNestedElement(fieldElement);
        } else {
            fieldElement.setParent(itemsElement);
            itemsElement.setNestedElement(fieldElement);
        }
    }


    /*
    * 生成对应的xml 解析控件
    * */
    private ApesElement createApesElement(String apesType, ApesParser apesParser) {
        ApesElement apesElement = new SimpleApesElement();
        apesElement.setName(apesType);
        apesElement.setParser(apesParser);
        return apesElement;
    }

    /*
    * 创建条件记录
    * */
    public Object create(SimpleRequest simpleRequest) {
        String className = getClassNameByRequest(simpleRequest);
        Object entity = getEntityByRequestAndClassName(simpleRequest, className);

        Date now = DateUtil.getDate(DateUtil.SHOW_DATE_FORMAT);
        Date start = getStart(entity);
        Date end = getEnd(entity);
        if (start.getTime() > end.getTime()) {
            throw new RuntimeException("开始时间不允许大于结束时间！");
        }
        if (end != null && end.getTime() < now.getTime()) {
            throw new RuntimeException("结束时间不允许小于当前时间！");
        }

        ReflectUtil.setField(entity, "state", "sent");
        if (entity instanceof BaseModel) {
            ((BaseModel) entity).setWrite(true);
        }
        return JpaUtil.getRepository(className).saveAndFlush(entity);
    }


    /*
    * 条件记录执行
     * */
    public Object executive(SimpleRequest simpleRequest) throws Exception {
        String className = getClassNameByRequest(simpleRequest);
        Object entity = getEntityByRequestAndClassName(simpleRequest, className);
        ReflectUtil.setField(entity, "state", "approve");
        ReflectUtil.setField(entity, "approveDate", new Date());
        ReflectUtil.setField(entity, "approveUid", simpleRequest.getPerson());
        createConditionRecord(entity); //创建条件记录（执行表）
        entity = JpaUtil.getRepository(className).saveAndFlush(entity);
        if (entity instanceof BaseModel) {
            ((BaseModel) entity).setWrite(true);
        }
        return entity;
    }

    /*
    * 终止条件记录
    * */
    public Object stop(SimpleRequest simpleRequest) {
        String className = getClassNameByRequest(simpleRequest);
        Object entity = getEntityByRequestAndClassName(simpleRequest, className);
        ReflectUtil.setField(entity, "state", "stop");
        ReflectUtil.setField(entity, "stopDate", new Date());
        ReflectUtil.setField(entity, "stopUid", simpleRequest.getPerson());
        deleteConditionRecord(entity);
        entity = JpaUtil.getRepository(className).saveAndFlush(entity);
        if (entity instanceof BaseModel) {
            ((BaseModel) entity).setWrite(true);
        }
        return entity;
    }


    /**
     * 创建条件记录（执行表）
     */
    private void createConditionRecord(Object entity) throws Exception {
        ConditionTable conditionTable = getConditionTable(entity);
        Set items = ReflectUtil.getFieldValue(entity, "items");
        String masterClassName = conditionTable.getClassName(conditionTable.masterExecClassName());

        List records = new ArrayList();
        items.stream().forEach(item -> {
            Object master = JpaUtil.newEntityInstance(masterClassName);
            if (master == null) return;
            ReflectUtil.copyAll(entity, master, "id");
            ReflectUtil.copyAll(item, master, "id");
            if (conditionTable.isStepped()) {
                Set slaveItems = new HashSet();
                String slaveClassName = conditionTable.getClassName(conditionTable.slaveExecClassName());
                conditionTable.getConditionTableSteppeds().stream().forEach(conditionTableStepped -> {
                    Double stepValue = ReflectUtil.getFieldValue(item, conditionTableStepped.stepKeyField());
                    Double value = ReflectUtil.getFieldValue(item, conditionTableStepped.stepValueField());
                    if (stepValue != null && stepValue != 0 && value != null && value != 0) {
                        Object slave = JpaUtil.newEntityInstance(slaveClassName);
                        ReflectUtil.setField(slave, ConditionField.STEP_FIELD_NAME, stepValue);
                        ReflectUtil.setField(slave, ConditionField.AMOUNT_FIELD_NAME, value);
                        slaveItems.add(slave);
                    }
                });
                ReflectUtil.setField(master, "items", slaveItems);
            }
            ReflectUtil.setField(master, "source", ReflectUtil.getFieldValue(entity, "id"));

            Date now = DateUtil.getDate(DateUtil.SHOW_DATE_FORMAT);
            Date start = getStart(master);
            if (start != null && start.getTime() < now.getTime()) {
                setStart(master, now);
            }
            Date end = getEnd(master);
            if (end != null && end.getTime() < now.getTime()) {
                throw new RuntimeException("结束时间不允许小于当前时间！");
            }
            union(conditionTable, master);
            records.add(master);
        });
        JpaUtil.getRepository(masterClassName).save(records);
    }

    private void union(ConditionTable conditionTable, Object master) {
        List crs = conditionTable.findConditionRecord(master, false);
        if(crs == null) return;
        List<Bucket> buckets = new ArrayList<>();
        crs.stream().forEach(cr -> {
            Bucket bucket = createBucket(cr);
            buckets.add(bucket);
        });
        buckets.add(createBucket(master));
        Bucket union = Bucket.union(buckets);
        if (union == null) return;
        crs.stream().filter(cr -> getStart(cr).getTime() <= union.getStartTime() && getEnd(cr).getTime() >= union.getEndTime())
                .forEach(cr -> {
                    List rows = new ArrayList();
                    CustomRepository repository = getExecRepository(cr);
                    Date now = DateUtil.getDate(DateUtil.SHOW_DATE_FORMAT);
                    if (getStart(cr).getTime() < union.getStartTime()) {
                        Date start = getStart(cr);
                        Date end = DateUtil.add(union.getStart(), Calendar.DATE, -1);
                        if (end.getTime() >= now.getTime()) {
                            cloneConditionRecord(cr, rows, start, end);
                        }
                    }
                    if (getEnd(cr).getTime() > union.getEndTime()) {
                        Date start = DateUtil.add(union.getEnd(), Calendar.DATE, 1);
                        Date end = getEnd(cr);
                        if (end.getTime() >= now.getTime()) {
                            cloneConditionRecord(cr, rows, start, end);
                        }
                    }
                    repository.delete(cr);
                    if (rows.size() > 0) repository.save(rows);
                });
        union(conditionTable, master);
    }

    private void cloneConditionRecord(Object cr, List rows, Date start, Date end) {
        Object o = cloneConditionRecord(cr);
        setStart(o, start);
        setEnd(o, end);
        rows.add(o);
    }

    private Bucket createBucket(Object cr) {
        Date start = getStart(cr);
        Date end = getEnd(cr);
        return new Bucket(start, end);
    }

    private void setStart(Object cr, Date now) {
        ReflectUtil.setField(cr, ConditionField.START_DATE_FIELD_NAME, now);
    }

    private Date getStart(Object cr) {
        return ReflectUtil.getFieldValue(cr, ConditionField.START_DATE_FIELD_NAME);
    }

    private void setEnd(Object cr, Date now) {
        ReflectUtil.setField(cr, ConditionField.END_DATE_FIELD_NAME, now);
    }

    private Date getEnd(Object cr) {
        return ReflectUtil.getFieldValue(cr, ConditionField.END_DATE_FIELD_NAME);
    }


    private Object cloneConditionRecord(Object source) {
        Object target = ReflectUtil.clone(source, "id", "items");
        if (target == null) return null;
        Set items = ReflectUtil.getFieldValue(source, "items");
        if (items == null) return target;
        Set slaveItems = new HashSet();
        items.stream().forEach(item -> {
            Object targetItem = ReflectUtil.clone(item, "id", "master");
            slaveItems.add(targetItem);
        });
        ReflectUtil.setField(target, "items", slaveItems);
        return target;
    }


    /**
     * 删除条件记录（执行表）
     */
    private void deleteConditionRecord(Object entity) {
        ConditionTable conditionTable = getConditionTable(entity);
        String masterClassName = conditionTable.getClassName(conditionTable.masterExecClassName());
        CustomRepository repository = JpaUtil.getRepository(masterClassName);
        Specification<ConditionTable> criteria = JpaDsl.toCriteriaByEq("source", ReflectUtil.getFieldValue(entity, "id"));
        repository.deleteAll(repository.findAll(criteria));
    }

    private CustomRepository getExecRepository(Object entity) {
        return JpaUtil.getRepository(entity.getClass().getName());
    }

    private ConditionTable getConditionTable(Object entity) {
        return ReflectUtil.getFieldValue(entity, "conditionTable");
    }

    /*
    * 获取 className
    * */
    private String getClassNameByRequest(SimpleRequest simpleRequest) {
        Map requestObject = simpleRequest.getMap();
        Map conditionTableMap = (Map) requestObject.get("conditionTable");
        String conditionTableId = (String) conditionTableMap.get("id");
        return getClassNameByConditionTableId(conditionTableId);
    }


    private String getClassNameByConditionTableId(String conditionTableId) {
        // 条件表
        ConditionTable conditionTable = conditionTableRepository.findOne(conditionTableId);
        return conditionTable.getClassName(conditionTable.masterClassName());
    }


    private Object getEntityByRequestAndClassName(SimpleRequest simpleRequest, String className) {
        return simpleRequest.getO(JpaUtil.getDomainClass(className));
    }


    /*
    * 动态查找
    * */
    public Page<Object> findAll(SimpleRequest simpleRequest) {
        Map map = simpleRequest.getMap();
        List<Map<String, Object>> condition = (List<Map<String, Object>>) map.get("condition");

        List<Map<String, Object>> newCondition = condition.stream().filter(
                (item) -> !"conditionTable".equals(item.get("field").toString())
        ).collect(Collectors.toList());

        String conditionTableId = condition.stream().filter(
                (item) -> "conditionTable".equals(item.get("field").toString())
        ).findFirst().get().get("value").toString();

        return JpaUtil.getRepository(getClassNameByConditionTableId(conditionTableId)).findAll(JpaDsl.toCriteria(newCondition), PageRequest.of(simpleRequest.get("page"), simpleRequest.get("size")));
    }


}
